package abacab.agent.actor;

import abacab.actor.Actor;

/**
 * Represents an actor working on shared objects
 * 
 * @param <M>
 *          the type of handled message
 */
@SuppressWarnings("rawtypes")
public abstract class Agent<M> extends Actor<M> {
	/** The monitor for the locking mechanism */
	private static final Object	MONITOR	= new Object();

	/** The shared objects */
	private SharedObject[]			sharedObjects;

	/** Indicates if the agent is ready for execution */
	private int									readyForExecution;

	/**
	 * Create the agent
	 */
	protected Agent() {
		this.readyForExecution = -1;
	}

	@Override
	public void init() {
		super.init();
		this.sharedObjects = getSharedObjects();
	}

	/**
	 * Get the objects shared with other agents.<br />
	 * This method is called during {@link #init()}.
	 * 
	 * @return the objects shared with other agents
	 */
	protected abstract SharedObject[] getSharedObjects();

	@Override
	public final boolean isReadyForExecution() {
		synchronized (MONITOR) {
			if (this.readyForExecution < 0) {
				this.readyForExecution = this.sharedObjects.length;
				for (SharedObject so : this.sharedObjects) {
					this.readyForExecution -= so.lock(this);
				}
			}
			return this.readyForExecution == 0;
		}
	}

	@Override
	public final void afterExecution() {
		synchronized (MONITOR) {
			for (SharedObject so : this.sharedObjects) {
				so.unlock();
			}
			this.readyForExecution = -1;
		}
	}

	/**
	 * Notify that a lock was acquired. This method is called when a shared object is unlocked by an other agent in the call to {@link #afterExecution()}. It is
	 * therefore already synchronized on the {@link #MONITOR}.
	 */
	final void lockAcquired() {
		this.readyForExecution--;
	}
}